﻿using ComData;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace LLCVApi
{
    public class LLCV
    {
        #region base
        private int ID = 0;
        public LLCV(int id)
        {
            ID = id;
            SetData = CVAPI.SetDataM;
            GetData = CVAPI.GetDataM;
            Process = CVAPI.ProcessM;

            pdata_int = Marshal.UnsafeAddrOfPinnedArrayElement(data_int, 0);
            pdata_float = Marshal.UnsafeAddrOfPinnedArrayElement(data_float, 0);
        }

        protected CVAPI.pfCVFunction SetData;
        protected CVAPI.pfCVFunction GetData;
        protected CVAPI.pfCVFunction Process;
        #endregion base

        #region data_check

        protected float[] data_float = new float[1];
        private IntPtr pdata_float = IntPtr.Zero;
        protected void CheckDataSize_Float(int data_size)
        {
            if (data_size > data_float.Length)
            {
                data_float = new float[data_size + 16];
            }
            pdata_float = Marshal.UnsafeAddrOfPinnedArrayElement(data_float, 0);
        }

        protected int[] data_int = new int[1];
        private IntPtr pdata_int = IntPtr.Zero;
        protected void CheckDataSize_Int(int data_size)
        {
            if (data_size > data_int.Length)
            {
                data_int = new int[data_size + 16];
            }
            pdata_int = Marshal.UnsafeAddrOfPinnedArrayElement(data_int, 0);
        }

        #endregion data_check

        #region

        public int SetDebug(bool debug_mode)
        {
            CheckDataSize_Int(8);
            data_int[0] = debug_mode ? 1 : 0;
            return SetData(ID, 0, pdata_int);
        }

        public int SetImageWHCh(int w, int h, int ch)
        {
            CheckDataSize_Int(8);
            data_int[0] = w;
            data_int[1] = h;
            data_int[2] = ch;
            return SetData(ID, 1, pdata_int);
        }

        public int SetFlip(int flip, int trans)
        {
            CheckDataSize_Int(8);
            data_int[0] = flip;
            data_int[1] = trans;
            return SetData(ID, 2, pdata_int);
        }

        public int SetROI(int x, int y, int w, int h)
        {
            CheckDataSize_Int(8);
            data_int[0] = x;
            data_int[1] = y;
            data_int[2] = w;
            data_int[3] = h;
            return SetData(ID, 3, pdata_int);
        }

        #region calibration
        public int SetCalibrationPts(List<float> xyuves)
        {
            CheckDataSize_Float(Math.Max(64, xyuves.Count));
            for (int i = 0; i < xyuves.Count; i++)
            {
                data_float[i] = xyuves[i];
            }
            return SetData(ID, 9, pdata_float);
        }

        public F2 GetRealPt(float ix, float iy)
        {
            CheckDataSize_Float(64);
            data_float[0] = ix;
            data_float[1] = iy;
            data_float[2] = ix;
            data_float[3] = iy;
            GetData(ID, 9, pdata_float);
            return new F2(data_float[2], data_float[3]);
        }

        #endregion calibration

        public int SetBackParams(int method, int binm, float binp)
        {
            CheckDataSize_Float(64);
            data_float[0] = method;
            data_float[1] = binm;
            data_float[2] = binp;
            return SetData(ID, 10, pdata_float);
        }

        public int SetBackMark(IntPtr data)
        {
            return SetData(ID, 11, data);
        }

        public int SetBackMarkWH(int w, int h)
        {
            CheckDataSize_Int(8);
            data_int[0] = w;
            data_int[1] = h;
            return SetData(ID, 12, pdata_int);
        }

        public int SetBackMarkFn(string markfn)
        {
            IntPtr pdata = Marshal.StringToHGlobalAnsi(markfn);
            int rtn = SetData(ID, 13, pdata);
            Marshal.FreeHGlobal(pdata);
            return rtn;
        }

        #region LMatch
        public int SetYamlPath(string yaml_dir)
        {
            IntPtr pdata = Marshal.StringToHGlobalAnsi(yaml_dir);
            int rtn = SetData(ID, 14, pdata);
            Marshal.FreeHGlobal(pdata);
            return rtn;
        }

        public int SetTemplateParam(int method = 2, int num_features = 64,
            float degree_s = -179.0F, float degree_e = 180.0F, float step_degree = 1.0F,
            int pyramid = 3, float weak_thresh = 30, float strong_thresh = 60)
        {
            CheckDataSize_Float(64);
            data_float[0] = method;
            data_float[1] = num_features;
            data_float[2] = weak_thresh;
            data_float[3] = strong_thresh;
            data_float[4] = pyramid;
            data_float[5] = degree_s;
            data_float[6] = degree_e;
            data_float[7] = step_degree;
            return SetData(ID, 15, pdata_float);
        }

        public int processMatch(int method, float similarity, int countTop, float focuse_x = -1, float focuse_y = -1)
        {
            _method = method;
            lock(this)ResultData.Clear();
            //MatchData.Clear();

            CheckDataSize_Float(64);
            data_float[0] = method;
            data_float[1] = similarity;
            data_float[2] = focuse_x;//focused
            data_float[3] = focuse_y;
            data_float[4] = countTop;
            data_float[5] = countTop * 4;

            Count_Result = Process(ID, 14, pdata_float);
            DTProcessed = DateTime.Now;

            return Count_Result;
        }
        public int GetMatchData(int markid)
        {
            int count = Count_Result;
            if (count <= 0) return -1;

            CheckDataSize_Float(18 * count);
            int rtn = GetData(ID, 14, pdata_float);
                List<ResultItem> list_dic = new List<ResultItem>();
            lock (this)
            {
                for (int i = 0; i < count; i++)
                {
                    int head = i * 18;
                    //MatchResult md = new MatchResult();
                    ResultItem md = new ResultItem();
                    md.center.X = data_float[head + 0];
                    md.center.Y = data_float[head + 1];
                    md.focus.X = data_float[head + 2];
                    md.focus.Y = data_float[head + 3];
                    md.pts[0].X = data_float[head + 4];
                    md.pts[0].Y = data_float[head + 5];
                    md.pts[1].X = data_float[head + 6];
                    md.pts[1].Y = data_float[head + 7];
                    md.pts[2].X = data_float[head + 8];
                    md.pts[2].Y = data_float[head + 9];
                    md.pts[3].X = data_float[head + 10];
                    md.pts[3].Y = data_float[head + 11];
                    md.similarity = data_float[head + 12];
                    md.degree = data_float[head + 13];
                    md.r_center.X = data_float[head + 14];
                    md.r_center.Y = data_float[head + 15];
                    md.r_focus.X = data_float[head + 16];
                    md.r_focus.Y = data_float[head + 17];
                    md.method = _method;
                    md.markData.X = MarkData.w;
                    md.markData.Y = MarkData.h;
                    md.markData.U = MarkData.radius;
                    md.markData.V = MarkData.degree;

                    //MatchData.Add(md);

                    ResultData.Add(md);
                    list_dic.Add(md.Clone());
                }
            }
            if (dic.ContainsKey(markid))
                dic[markid] = list_dic;
            else dic.Add(markid, list_dic);
            return 0;
        }
        #endregion LMatch

        public class MarkItem
        {
            public float radius;
            public float w;
            public float h;
            public float area;
            public float arc;
            public float degree;
        }

        public MarkItem MarkData = new MarkItem();
        public int ProcessBackMark(int method)
        {
            CheckDataSize_Float(64);
            data_float[0] = method;
            if (Process(ID, 10, pdata_float) == 0)
            {
                MarkData.radius = data_float[0];//radius
                MarkData.w = data_float[1];//w
                MarkData.h = data_float[2];//h
                MarkData.area = data_float[3];//area
                MarkData.arc = data_float[4];//arc
                MarkData.degree = data_float[5];//degree
                return 0;
            }
            return -1;
        }
        public int SetImageBack(IntPtr data)
        {
            return SetData(ID, 100, data);
        }

        public int SetImageFore(IntPtr data)
        {
            return SetData(ID, 101, data);
        }

        public DateTime DTProcessed = DateTime.Now;
        public int Count_Result = 0;
        private int _method = 0;
        public float proportion_workpieces = 0.0F;
        public int ProcessBack(int mode, int tol_per, int countTop = -1, float focuse_x = -1, float focuse_y = -1)
        {
            _method = mode;

            lock(this)ResultData.Clear();

            CheckDataSize_Float(64);
            data_float[0] = mode;//0只有背部，1只有顶部，2先背部，后顶部
            data_float[1] = (tol_per * 0.01F);
            data_float[2] = focuse_x;//focused
            data_float[3] = focuse_y;
            data_float[4] = countTop;
            Count_Result = Process(ID, 100, pdata_float);
            proportion_workpieces = data_float[10];
            DTProcessed = DateTime.Now;

            return Count_Result;
        }
        public string GetResultString()
        {
            string txt = DTProcessed.ToString("HH:mm:ss fff") + "  ";
            if (Count_Result > 0)
            {
                txt += "Found:" + Count_Result.ToString();
            }
            else
                txt += "NO Objects";
            return txt;
        }

        public class ResultItem
        {
            public F2 center = new F2();
            public F2 focus = new F2();
            public F2[] pts = new F2[4] { new F2(), new F2(), new F2(), new F2() };
            public float similarity;
            public float degree;

            public F2 r_center = new F2();
            public F2 r_focus = new F2();

            public float condition1;
            public float condition2;

            public int method = 0;
            public F4 markData = new F4();

            public string GetSendDataString()
            {
                return r_center.X.ToString("F2") + "," + r_center.Y.ToString("F2") + "," + degree.ToString("F2");
            }

            public ResultItem Clone()
			{
                ResultItem ri = new ResultItem();
                ri.center = center;
                ri.focus = focus;
                ri.pts[0] = pts[0];
                ri.pts[1] = pts[1];
                ri.pts[2] = pts[2];
                ri.pts[3] = pts[3];
                ri.similarity = similarity;
                ri.degree = degree;
                ri.r_center = r_center;
                ri.r_focus = r_focus;
                ri.condition1 = condition1;
                ri.condition2 = condition2;
                ri.method = method;
                ri.markData = markData.Clone();
                return ri;
			}

        }

        private Dictionary<int, List<ResultItem>> dic = new Dictionary<int, List<ResultItem>>();

        public List<ResultItem> GetRemains(int markid)
        {
            if (dic.ContainsKey(markid)) return dic[markid];
            return new List<ResultItem>();
        }
        public int GetCvData(int cmark, List<ResultItem> sendData, int count)
        {
            if(!dic.ContainsKey(cmark))
			{
                return 0;
            }

            int bc = sendData.Count;

            var lt = dic[cmark];
            for (int i = 0; i < count && i < lt.Count; i++)
            {
                sendData.Add(lt[i]);
            }

            int ac = sendData.Count - bc;
            lt.RemoveRange(0, sendData.Count - bc);
            return ac;
        }

        public void Clear()
        {
            lock (this)
            {
                ResultData.Clear();
                dic.Clear();
            }
        }

        public List<ResultItem> ResultData = new List<ResultItem>();
        public int GetResultItem(int markid)
        {
            int count = Count_Result;
            CheckDataSize_Float(8 * count);
            int rtn = GetData(ID, 100, pdata_float);
            List<ResultItem> list_dic = new List<ResultItem>();
            lock (this)
            {
                for (int i = 0; i < count; i++)
                {
                    ResultItem r = new ResultItem();
                    r.center.X = data_float[8 * i + 0];
                    r.center.Y = data_float[8 * i + 1];
                    r.r_center.X = data_float[8 * i + 2];
                    r.r_center.Y = data_float[8 * i + 3];
                    r.degree = data_float[8 * i + 4];
                    r.similarity = data_float[8 * i + 5];
                    r.condition1 = data_float[8 * i + 6];
                    r.condition2 = data_float[8 * i + 7];
                    r.method = _method;
                    r.markData.X = MarkData.w;
                    r.markData.Y = MarkData.h;
                    r.markData.U = MarkData.radius;
                    r.markData.V = MarkData.degree;

                    ResultData.Add(r);
                    list_dic.Add(r.Clone());
                }
            }
            if (dic.ContainsKey(markid))
                dic[markid] = list_dic;
            else dic.Add(markid, list_dic);
            return 0;
        }

        public int ProcessFore(int mode, int binmethod, float binparam)
        {
            CheckDataSize_Float(16);
            data_float[0] = mode;//0只有背部，1只有顶部，2先背部，后顶部
            data_float[1] = binmethod;//0:OTSU; 1:gray bin
            data_float[2] = binparam;

            int rtn = Process(ID, 101, pdata_float);
            if (rtn > 0)
            {
            }

            return rtn;
        }
        #endregion
    }
}
